#ifndef QUIMERA_PROTOCOL_V1_H
#define	QUIMERA_PROTOCOL_V1_H

#include<map>
#include"Frame.h"
#include <locale>
#include <string.h>
#include <stdio.h>
#include <time.h>

typedef struct Envelope{
    int length;
    void *buff;
}Envlp;

typedef struct Q_pool{
    unsigned int first;
    unsigned int last;
}Q_pool;

typedef struct t_locker{
    unsigned short sec;
    bool *lock;
}t_locker;

typedef struct Q_density{
    unsigned short cycle;
    unsigned char switchpop;   
}Q_density;

typedef struct cli_map{
    cli_map(){}
    cli_map(const cli_map &cm)
    {
        strcpy(ip,cm.ip);
        q_id=cm.q_id;
        n_register=cm.n_register;
        check_in=cm.check_in;
    }
    
    char                ip[16];
    unsigned int        q_id;
    unsigned int        n_register;
    time_t              check_in;
}cli_map;

typedef struct msg_map
{
    msg_map(){}
    msg_map(const msg_map &mm)
    {
        confirmation=mm.confirmation;
        message=mm.message;
        checkin_time=mm.checkin_time;
    }
    bool                confirmation;
    Message_queue       message;
    time_t              checkin_time;
    
}msg_map;

static const unsigned int MAX_BUFFER_SIZE=125000; 
static const unsigned short DEFAULT_INTVL=1;
static const int DEFAULT_BN=10;
static const int DEFAULT_WAIT=10000;
static const int U_SECONDS=1000000;


template <typename T, typename U>
class create_map
{
private:
    std::map<T, U> m_map;
public:
    create_map(const T& key, const U& val)
    {
        m_map[key] = val;
    }

    create_map<T, U>& operator()(const T& key, const U& val)
    {
        m_map[key] = val;
        return *this;
    }

    operator std::map<T, U>()
    {
        return m_map;
    }
};

/*typedef struct pool
{
    unsigned int first;
    unsigned int last;
};

typedef struct cli_map
{
    cli_map(){}
    cli_map(const cli_map &cm)
    {
        strcpy(ip,cm.ip);
        id=cm.id;
        checkin_time=cm.checkin_time;
    }
    char                ip[16];
    int                 id;
    date_time_type      checkin_time;
}; 

typedef struct msg_map
{
    msg_map(){}
    msg_map(const msg_map &mm)
    {
        conf=mm.conf;
        message=mm.message;
        checkin_time=mm.checkin_time;
        retransmission=mm.retransmission;
    }
    bool                conf;
    message_queue       message;
    date_time_type      checkin_time;
    short     cd Ne          retransmission;
    
};*/

class Quimera_methods;
class Quimera_utils;
class Quimera_server;
class Quimera_client;

class Quimera_methods
{
public: 
    virtual void Membership_procedure(Message_queue)=0;
    virtual void Investment_procedure(Message_queue)=0;
    virtual void Guard_report_procedure(Message_queue)=0;
    virtual void Missing_report_procedure(Message_queue)=0;
    virtual void Complaint_procedure(Message_queue)=0;
    virtual void Adoption_paper_procedure(Message_queue)=0;
    /*virtual void Guard_confirmation_procedure(void *)=0;
    virtual void Missing_report_procedure(void *)=0;
    virtual void Missing_confirmation_procedure(void *)=0;
    virtual void Adoption_paper_procedure(void *)=0;
    virtual void Bookmark_procedure(void *)=0;*/   
};

class Quimera_utils
{
public:
    unsigned short t_wait;
    bool locker;
    pthread_t init;
    void *args;
    t_locker lock_args;
    Message_queue msg_queue;
    Message_queue log_msg;
    static void* Background_t(void *);
    int Send(int &, struct msghdr &, struct iovec *, Envlp &);  
    int SendRecv(int &,const long &, unsigned short, unsigned int &, Package &);
    static bool interrupt;
};



class Quimera_server : public Quimera_methods, public Quimera_utils
{
public:
    unsigned char domain;
    unsigned int quimera_id;
    unsigned short f_time;
    Q_pool pool;
    Q_density q_dent;
    pthread_mutex_t mutex;
    pthread_t init;
    pthread_t init_2;
    int l_queue_id;
    int upkr_queue_id;
    
    void* handle;
    Message* (*create)();
    void (*destroy)(Message*);
    Message* MSG;
    Package *qpkg;
    void* p_this;
    Message_queue buff;
    Message_queue buff_a;
    char port[6];
    
    struct msghdr mh;
    struct iovec v[1];
    int sockfd;
    Envlp envelope;
    time_t time_now;
    
    std::string h_seed;
    std::locale loc;
    long clhash;
    long m_ghash;
    
    cli_map cmap;
    msg_map m_gmap;
            
    std::map<long,cli_map>::iterator ti;
    std::map<long,cli_map> clist;
    
    std::map<long,msg_map>::iterator mgit;
    std::map<long,msg_map> mglist;
    
    static bool interrupt;
    static bool quality_control;
    static unsigned int ids_pointer;
   
    Quimera_server(unsigned char &,Q_pool &,unsigned short &, Q_density &);
    virtual ~Quimera_server();
    
    long C_hash(std::string &,const std::collate<char>&);
    unsigned int Give_me_ID(struct sockaddr_in &, unsigned int &);
    unsigned int Get_index(bool *);
    static void *Serial_killer(void *);
    static void *Complaint_killer(void *t);
    int Listen();
    

    void Membership_procedure(Message_queue);
    void Investment_procedure(Message_queue);
    void Guard_report_procedure(Message_queue);
    void Missing_report_procedure(Message_queue);
    void Complaint_procedure(Message_queue);
    void Adoption_paper_procedure(Message_queue);
    /*void Guard_report_procedure(void *);
    void Guard_confirmation_procedure(void *);
    void Missing_report_procedure(void *);
    void Missing_confirmation_procedure(void *);
    void Adoption_paper_procedure(void *);
    void Bookmark_procedure(void *);*/
};
  /*  
    unsigned int Give_me_ID(struct sockaddr_in &id_client)
    { 
        const std::collate<char>& coll = std::use_facet<std::collate<char> >(this->loc);
        if((ids.first+ids_pointer)>=ids.last){
           std::cout<<"Error pool limits"<<std::endl;
        }
        else{
           ids_pointer=ids_pointer+1;
           this->cmap.id=ids.first+ids_pointer;
           strcpy(this->cmap.ip,inet_ntoa(id_client.sin_addr)); 
           header.Get_time_message(this->cmap.checkin_time);
           this->h_seed=this->cmap.ip;
           this->myhash = coll.hash(this->h_seed.data(),this->h_seed.data()+this->h_seed.length());
           ti=clist.begin();
           clist.insert(std::pair<long,cli_map>(myhash,this->cmap));
        }
        std::cout<<"La lista de cliente es:\n";
        for (ti=clist.begin() ; ti!= clist.end(); ti++)
        std::cout << (*ti).first << "=>"<<(*ti).second.ip <<"=>"<<(*ti).second.id<<std::endl;
        return(ids.first+ids_pointer);
   }
   void Adoption_paperwork(message_queue &parent, message_queue &guard)
   {
        Header header(version,domain,id_server,parent.package.header.id_server_source);
        Adoption_papers adopt(header,guard.package,guard.cli_addr);
        this->envelop.package=adopt.message;
        this->envelop.cli_addr=parent.cli_addr;
        this->envelop.id_message=HIGHT_PRIORITY;
        if (msgsnd(packer_queue_id, (struct msgbuf *)&this->envelop,
               sizeof(this->envelop.package)+sizeof(this->envelop.cli_addr),IPC_NOWAIT)<0)
        std::cout<<"Error, packer queue might not have enough memory"<<std::endl;
       
   }
    
void Package_processor(message_queue);

private:
    void Membership_procedure(struct sockaddr_in &);
    void Investment_procedure(package &){};
    void Guard_report_procedure(message_queue &, long &);
    void Guard_confirmation_procedure(long &){};
    void Missing_report_procedure(message_queue &, long &);
    void Missing_confirmation_procedure(long &){};
    void Adoption_paper_procedure(message_queue &){};
   
    Header header;
    pool ids;
    unsigned int id_server;
    unsigned char domain;
    date_time_type flush;
    static int ids_pointer;
    int packer_queue_id;
    bool conf;
    message_queue envelop;
    message_queue confirmation;
    struct msqid_ds msqid_ds, *buf;
    char port[6];
    std::string h_seed;
    long myhash;
    long clhash;
    cli_map cmap;
    msg_map mmap;
    std::locale loc;
    std::map<long,msg_map>::iterator git;
    std::map<long,msg_map> glist;
    std::pair<std::map<long,msg_map>::iterator,bool>gret;
    
    std::map<long,msg_map>::iterator mit;
    std::map<long,msg_map> mlist;
    std::pair<std::map<long,msg_map>::iterator,bool>mret;
    
    std::map<long,cli_map>::iterator ti;
    std::map<long,cli_map> clist;
    std::pair<std::map<long,cli_map>::iterator,bool>ter;
   
  //  friend void Adoption_paperwork(message_queue&, message_queue&);
    
};

int Quimera_server::ids_pointer=0;

inline void Quimera_server::Package_processor(message_queue message)
{ 

    const std::collate<char>& coll = std::use_facet<std::collate<char> >(this->loc);  
    this->h_seed=inet_ntoa(message.cli_addr.sin_addr);
    this->clhash=coll.hash(this->h_seed.data(),this->h_seed.data()+this->h_seed.length());
    ti=clist.find(this->clhash);
    if(message.package.header.version!=version)
        std::cout << "Error, version missmatch" << std::endl;
    else if(message.package.header.domain!=domain)
        std::cout << "Error, domain missmatch" << std::endl;
    else if(message.package.header.type_message==MEMBERSHIP)
        Membership_procedure(message.cli_addr);
    else if(ti==clist.end())
    {
        std::cout << "This client is not a member of this domain" << std::endl;
    }    
    else{
                
        this->h_seed=inet_ntoa(message.package.id_client.sin_addr);        
        sprintf(this->port,"%d",ntohs(message.package.id_client.sin_port)); 
        this->h_seed+=this->port;
        this->myhash=coll.hash(this->h_seed.data(),this->h_seed.data()+this->h_seed.length());
        std::cout<<"la semilla: "<<this->h_seed<<std::endl;
        switch(message.package.header.type_message){
       
                    case GUARD_REPORT:{ 
                         Guard_report_procedure(message,this->myhash);
                         break;
                    }
                    case MISSING_REPORT:{ 
                         Missing_report_procedure(message,this->myhash);
                         break;
                    }
                      
                    
                    default:{
                        std::cout << "fallo" << std::endl;        
                        break;
                    }
                    
                
                }
        }
}

inline void Quimera_server::Membership_procedure(struct sockaddr_in &cli_addr)
{
    unsigned int id_source=Give_me_ID(cli_addr);
    Header header(version,domain,id_server,id_source);
    Investment ivmt(header,flush);
    this->envelop.package=ivmt.message;
    this->envelop.cli_addr=cli_addr;
    this->envelop.id_message=LOW_PRIORITY;
    if (msgsnd(packer_queue_id, (struct msgbuf *)&this->envelop,
               sizeof(this->envelop.package)+sizeof(this->envelop.cli_addr),IPC_NOWAIT)<0)
        std::cout<<"Error, packer queue might not have enough memory"<<std::endl;              
}

inline void Quimera_server::Guard_report_procedure(message_queue &message, long &hash)
{  
   
    
   std::cout<<"hash en guard: "<<hash<<std::endl;
   git=glist.find(hash);
   if(git==glist.end())
   {
        this->mmap.retransmission=0;
        this->mmap.message=message;
        header.Get_time_message(this->mmap.checkin_time);
        git=glist.begin();
        glist.insert(std::pair<long,msg_map>(this->myhash,this->mmap));
        
        message.package.ip_server_guardian.sin_addr=message.cli_addr.sin_addr;
        Guard guard_cfm(message.package);
        this->confirmation.package=guard_cfm.message;
        this->confirmation.cli_addr=message.cli_addr;
        this->confirmation.id_message=HIGHT_PRIORITY;
        if(msgsnd(packer_queue_id, (struct msgbuf *)&this->confirmation,
        sizeof(this->confirmation.package)+sizeof(this->confirmation.cli_addr),IPC_NOWAIT)<0)
        {
            this->mmap.conf=false;    
            std::cout<<"Error, packer queue might not have enough memory"<<std::endl;
        }
        else
            this->mmap.conf=true;
   }
   mit=mlist.find(hash);
   if(mit!=mlist.end())
       Adoption_paperwork((*mit).second.message,message);    

}

inline void Quimera_server::Missing_report_procedure(message_queue &message, long &hash)
{
   std::cout<<"hash en missing: "<<hash<<std::endl;
    mit=mlist.find(hash);
   if(mit==mlist.end())
   {
        this->mmap.retransmission=0;
        this->mmap.message=message;
        header.Get_time_message(this->mmap.checkin_time);
        mit=mlist.begin();
        mlist.insert(std::pair<long,msg_map>(this->myhash,this->mmap)); 
       
        Missing_report missing_cfm(message.package);
        this->confirmation.package=missing_cfm.message;
        this->confirmation.cli_addr=message.cli_addr;
        this->confirmation.id_message=MEDIUM_PRIORITY;
        
        if(msgsnd(packer_queue_id, (struct msgbuf *)&this->confirmation,
        sizeof(this->confirmation.package)+sizeof(this->confirmation.cli_addr),IPC_NOWAIT)<0)
        {
                this->mmap.conf=false;
                std::cout<<"Error, packer queue might not have enough memory"<<std::endl;
        }
   }
   
   git=glist.find(hash);
   if(git!=glist.end())
       Adoption_paperwork(message,(*git).second.message);

}
*/
//********************************************************************************************

class Quimera_client : public Quimera_methods, public Quimera_utils
{
public:
    unsigned char domain;
    unsigned int quimera_id;
    unsigned short f_time;
    unsigned int id_root_serv;
    struct sockaddr_in ip_root_serv;
    pthread_mutex_t mutex;
    
    int l_queue_id;
    int upkr_queue_id;
    int api_queue_id;
    
    unsigned short interval;
    unsigned int beacon_number;
    unsigned int beacon_count;
    
    Package *qpkg;
    Package rcv;
    Package ref;
    
    Message_queue buff;
  
    void *p_this;
    struct msghdr mh;
    struct iovec v[1];
    int sockfd;
    Envlp envelope;
    
    void* handle;
    Message* (*create)();
    void (*destroy)(Message*);
    Message* MSG;
    
    static std::map<unsigned int,bool>::iterator tic;
    static std::map<unsigned int,bool> iclist;
    
    
    bool strike;
    static bool quality_control;
    static bool online;
    static bool interrupt;
    
 
    Quimera_client(const Quimera_client &);
    Quimera_client(unsigned char &, struct sockaddr_in &, unsigned short &, unsigned int &);
    virtual ~Quimera_client();
    
    int Connect();
    void Abort();
    void C_success_report();
    static void* Keep_intouch(void *); 
    int Listen();
    void Membership_procedure(Message_queue);
    void Investment_procedure(Message_queue);
    void Guard_report_procedure(Message_queue);
    void Missing_report_procedure(Message_queue);
    void Complaint_procedure(Message_queue);
    void Adoption_paper_procedure(Message_queue);
    /*void Investment_confirmation_procedure(void *);
    void Guard_report_procedure(void *);
    void Guard_confirmation_procedure(void *);
    void Missing_report_procedure(void *);
    void Missing_confirmation_procedure(void *);
    void Adoption_paper_procedure(void *);
    void Bookmark_procedure(void *);*/
 };
 

 typedef struct Thd_args{
    
    Thd_args(unsigned short&, int&, Package&, Quimera_client&);
    unsigned short intvl;
    int beacon_number;
    Package ref;
    Quimera_client x;
}Thd_args;
 

    /*
    int packer_queue_id;
    int i_queue_id;
    message_queue envelop;
    message_queue confirmation;
    date_time_type flush;
    struct sockaddr_in ip_root_serv;
    Header header;
    char port[6];
    std::string h_seed;
    long myhash;
    msg_map mmap;
    std::locale loc;
    std::map<long,msg_map>::iterator git;
    std::map<long,msg_map> glist;
    std::pair<std::map<long,msg_map>::iterator,bool>gret;
    
    std::map<long,msg_map>::iterator mit;
    std::map<long,msg_map> mlist;
    std::pair<std::map<long,msg_map>::iterator,bool>mret;
    
    
  
   / 
    void Package_processor(message_queue);
    
private:
    void Membership_procedure(struct sockaddr_in &){};
    void Investment_procedure(package &);
    void Guard_report_procedure(message_queue &, long &);
    void Guard_confirmation_procedure(long &);
    void Missing_report_procedure(message_queue &, long &);
    void Missing_confirmation_procedure(long &);
    void Adoption_paper_procedure(message_queue &);
    
    
    
};

inline void Quimera_client::Package_processor(message_queue message)
{ 

    const std::collate<char>& coll = std::use_facet<std::collate<char> >(this->loc);  
    this->h_seed=inet_ntoa(message.package.id_client.sin_addr);
    sprintf(this->port,"%d",ntohs(message.package.id_client.sin_port)); 
    this->h_seed+=this->port;
    this->myhash=coll.hash(this->h_seed.data(),this->h_seed.data()+this->h_seed.length());

    if(message.package.header.version!=version)
        std::cout <<"Error, version missmatch" << std::endl;
    else if(message.package.header.domain!=domain)
        std::cout <<"Error, domain missmatch" << std::endl;
    else if(message.package.header.id_server_destination!=quimera_id)
        std::cout<<"Error, this message is not destinate to this client"<<std::endl;
    else{
                
                switch(message.package.header.type_message){
       
                    case INVESTMENT:{ 
                         Investment_procedure(message.package);
                         break;
                    }
                    case GUARD_REPORT:{
                         Guard_report_procedure(message,this->myhash);
                         break;
                    }
                    case GUARD_CONFIRMATION:{
                         Guard_confirmation_procedure(this->myhash);
                         break;
                    }
     
                     case MISSING_REPORT:{
                         Missing_report_procedure(message,this->myhash);
                         break;
                    }
                     case MISSING_CONFIRMATION:{
                         Missing_confirmation_procedure(this->myhash);
                         break;
                    }
                     
                     case ADOPTION_PAPERS:{
                         Adoption_paper_procedure(message);
                         break;
                     }
                     case BOOKMARK:{
                         Adoption_paper_procedure(message);
                         break;
                     }
                    default:{
                        std::cout << "fallo" << std::endl;        
                        break;
                    }
                    
                
                }
        }
}

inline void Quimera_client::Investment_procedure(package &message)
{
    quimera_id=message.header.id_server_destination;
    id_root_serv=message.header.id_server_source;
    flush=message.date_time;
    std::cout<<quimera_id<<std::endl;
    std::cout<<id_root_serv<<std::endl; 
}

inline void Quimera_client::Guard_report_procedure(message_queue &message,long &hash)
{
    git=glist.find(hash);
    if(git==glist.end() and message.package.header.type_message==GUARD_REPORT)
    {
       this->mmap.conf=false;
       this->mmap.retransmission=0;
       this->mmap.message=message;
       header.Get_time_message(this->mmap.checkin_time);
       git=glist.begin();
       glist.insert(std::pair<long,msg_map>(this->myhash,this->mmap));
       
       message.package.header.domain=domain;
       message.package.header.offset=sizeof(header.size.guard);
       message.package.header.flag=0;
       message.package.header.id_server_source=quimera_id;
       message.package.header.id_server_destination=id_root_serv;
       message.package.id_server_guardian=quimera_id;
     
       message.package.date_time=this->mmap.checkin_time;
       this->envelop.package=message.package;
       this->envelop.cli_addr=ip_root_serv;
       this->envelop.id_message=HIGHT_PRIORITY;
       if (msgsnd(packer_queue_id, (struct msgbuf *)&this->envelop,
           sizeof(this->envelop.package)+sizeof(this->envelop.cli_addr),IPC_NOWAIT)<0)
        std::cout<<"Error, packer queue might not have enough memory"<<std::endl;
       
    }
      
}

inline void Quimera_client::Guard_confirmation_procedure(long &hash)
{
    git=glist.find(hash);
    (*git).second.conf=true;
}

inline void Quimera_client::Missing_report_procedure(message_queue &message,long &hash)
{
    mit=mlist.find(hash);
    if(mit==mlist.end())
    {
       this->mmap.conf=false;
       this->mmap.retransmission=0;
       this->mmap.message=message;
       header.Get_time_message(this->mmap.checkin_time);
       mit=mlist.begin();
       mlist.insert(std::pair<long,msg_map>(this->myhash,this->mmap));
       
       message.package.header.domain=domain;
       message.package.header.offset=sizeof(header.size.missing);
       message.package.header.flag=0;
       message.package.header.id_server_source=quimera_id;
       message.package.header.id_server_destination=id_root_serv;
       message.package.id_server_guardian=quimera_id;
     
       message.package.date_time=this->mmap.checkin_time;
       this->envelop.package=message.package;
       this->envelop.cli_addr=ip_root_serv;
       this->envelop.id_message=HIGHT_PRIORITY;
       if (msgsnd(packer_queue_id, (struct msgbuf *)&this->envelop,
           sizeof(this->envelop.package)+sizeof(this->envelop.cli_addr),IPC_NOWAIT)<0)
        std::cout<<"Error, packer queue might not have enough memory"<<std::endl;   
    }
    
}

inline void Quimera_client::Missing_confirmation_procedure(long &hash)
{
     mit=mlist.find(hash);
     (*mit).second.conf=true;
}

inline void Quimera_client::Adoption_paper_procedure(message_queue &message)
{
     if(msgsnd(i_queue_id, (struct msgbuf*)&message,
     sizeof(message.package)+sizeof(message.cli_addr),IPC_NOWAIT)<0)
        std::cout<<"Error, packer queue might not have enough memory"<<std::endl;

}*/
#endif	/* QUIMERA_PROTOCOL_V1_H */